package com.ruoyi.framework.sso;

import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson2.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.sso.domain.UserCenterLoginResult;
import com.ruoyi.framework.sso.domain.UserCenterTokenResponse;
import com.ruoyi.framework.sso.domain.UserCenterUserInfo;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.framework.web.service.SysPermissionService;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.service.ISysUserService;

/**
 * 用户中心登录流程编排
 */
@Service
public class UserCenterAuthService
{
    private static final Logger log = LoggerFactory.getLogger(UserCenterAuthService.class);
    /** 普通用户角色ID */
    private static final Long COMMON_ROLE_ID = 2L;
    /** 用户中心自动注册的初始密码 */
    private static final String DEFAULT_INIT_PASSWORD = "123456";

    private final UserCenterClient userCenterClient;
    private final ISysUserService userService;
    private final SysPermissionService permissionService;
    private final TokenService tokenService;
    private final SysLoginService loginService;
    private final RedisCache redisCache;

    public UserCenterAuthService(UserCenterClient userCenterClient, ISysUserService userService,
                                 SysPermissionService permissionService, TokenService tokenService,
                                 SysLoginService loginService, RedisCache redisCache)
    {
        this.userCenterClient = userCenterClient;
        this.userService = userService;
        this.permissionService = permissionService;
        this.tokenService = tokenService;
        this.loginService = loginService;
        this.redisCache = redisCache;
    }

    /**
     * 根据授权码发起统一登录
     */
    public UserCenterLoginResult loginByCode(String code, String redirectUri) {
        if (StringUtils.isBlank(code))
        {
            throw new ServiceException("code 不能为空");
        }

        UserCenterTokenResponse tokenResponse = userCenterClient.exchangeByCode(code,
            StringUtils.defaultString(redirectUri));
        UserCenterUserInfo userInfo = userCenterClient.getUserInfo(tokenResponse.getAccessToken());
        //log.info("userInfo：{}", JSONObject.toJSONString(userInfo));
        SysUser user = resolveLocalUser(userInfo);
        log.info("user：{}", JSONObject.toJSONString(user));
        LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user,
            permissionService.getMenuPermission(user));
        loginService.recordLoginInfo(user.getUserId());
        String localToken = tokenService.createToken(loginUser);
        cacheToken(user.getUserId(), tokenResponse);
        return new UserCenterLoginResult(localToken, tokenResponse, userInfo);
    }

    /**
     * 退出时同步注销用户中心会话
     */
    public void logout(LoginUser loginUser)
    {
        if (loginUser == null)
        {
            return;
        }
        String accessKey = CacheConstants.USER_CENTER_ACCESS_TOKEN_KEY + loginUser.getUserId();
        String accessToken = redisCache.getCacheObject(accessKey);
        if (StringUtils.isNotBlank(accessToken))
        {
            userCenterClient.logout(accessToken);
            redisCache.deleteObject(accessKey);
        }
        String refreshKey = CacheConstants.USER_CENTER_REFRESH_TOKEN_KEY + loginUser.getUserId();
        redisCache.deleteObject(refreshKey);
    }

    private SysUser resolveLocalUser(UserCenterUserInfo userInfo)
    {
        if (userInfo == null)
        {
            throw new ServiceException("用户中心未返回用户信息");
        }
        SysUser user = null;
        if (StringUtils.isNotBlank(userInfo.getPreferredUsername()))
        {
            user = userService.selectUserByUserName(userInfo.getPreferredUsername());
        }
        if (Objects.isNull(user) && StringUtils.isNotBlank(userInfo.getPhone()))
        {
            user = userService.selectUserByUserName(userInfo.getPhone());
        }
        if (user == null)
        {
            user = registerUserFromUserCenter(userInfo);
        }
        return user;
    }

    private SysUser registerUserFromUserCenter(UserCenterUserInfo userInfo)
    {
        String preferredUsername = userInfo.getPreferredUsername();
        if (StringUtils.isBlank(preferredUsername))
        {
            throw new ServiceException("用户中心账号缺少用户名，无法自动注册");
        }
        SysUser sysUser = new SysUser();
        sysUser.setUserName(preferredUsername);
        sysUser.setNickName(preferredUsername);
        sysUser.setPhonenumber(StringUtils.defaultString(userInfo.getPhone()));
        sysUser.setStatus(UserConstants.NORMAL);
        sysUser.setPassword(SecurityUtils.encryptPassword(DEFAULT_INIT_PASSWORD));
        sysUser.setPostIds(new Long[0]);
        sysUser.setRoleIds(new Long[] { COMMON_ROLE_ID });
        int rows = userService.insertUser(sysUser);
        if (rows <= 0)
        {
            throw new ServiceException("自动注册用户失败，请联系管理员");
        }
        return sysUser;
    }

    private void cacheToken(Long userId, UserCenterTokenResponse tokenResponse)
    {
        if (userId == null || tokenResponse == null)
        {
            return;
        }
        long accessExpire = defaultExpire(tokenResponse.getExpiresIn(), Duration.ofHours(5).getSeconds());
        redisCache.setCacheObject(CacheConstants.USER_CENTER_ACCESS_TOKEN_KEY + userId, tokenResponse.getAccessToken(),
            Math.toIntExact(accessExpire), TimeUnit.SECONDS);
        long refreshExpire = defaultExpire(tokenResponse.getRefreshTokenExpiresIn(), Duration.ofDays(7).getSeconds());
        if (StringUtils.isNotEmpty(tokenResponse.getRefreshToken()))
        {
            redisCache.setCacheObject(CacheConstants.USER_CENTER_REFRESH_TOKEN_KEY + userId,
                tokenResponse.getRefreshToken(), Math.toIntExact(refreshExpire), TimeUnit.SECONDS);
        }
    }

    private long defaultExpire(Long rawExpire, long defaultValue)
    {
        if (rawExpire == null || rawExpire <= 0)
        {
            return defaultValue;
        }
        return rawExpire;
    }
}
